# see copyright and information at http://code.google.com/p/perl-ctx/
# err codes :
# 0 - stoped by user
# 10 - connection unexpectedly closed
# 11 - no html form found or url mising or url missing;

package Net::Coro::HTTP;
use common::sense;
our $VERSION = 0.04;

use Carp;
use EV;
use Coro;
use Coro::EV;

use Net::Coro::EV::HTTP;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(async);

use Net::Coro::Proxy;
use Net::Coro::EV::Connect::Socks5;
use Net::Coro::EV::Connect::Proxy;


*BINDIP = \$Net::Coro::EV::HTTP::BINDIP;
*REPEAT = \$Net::Coro::EV::HTTP::REPEAT;
*TIMEOUT = \$Net::Coro::EV::HTTP::TIMEOUT;

*PROTOCOL = \$Net::Coro::EV::HTTP::PROTOCOL;
*REDIRECT = \$Net::Coro::EV::HTTP::REDIRECT;
*META_REFRESH = \$Net::Coro::EV::HTTP::META_REFRESH;
*HEADERS = \%Net::Coro::EV::HTTP::HEADERS;
*USER_AGENT = \$Net::Coro::EV::HTTP::HEADERS{'User-Agent'};

our $LWP = 0;
our $TRACE =0;
our $TRACE_URL =0;
our $TRACE_REQUEST =0;
our $TRACE_RESPONSE =0;

sub new {
	my ($class, %prm) = @_;
	my $socket;
	
	if (ref $prm{socket}) {
		$socket = delete $prm{socket}; 
	} else {
		$socket = Net::Coro::Proxy->new(delete($prm{socks5}), delete($prm{socks4}), delete($prm{proxy}), delete($prm{http_proxy}));
	}
	
	my $this = {};
	bless $this, $class;
	$this->{_HTTP} = Net::Coro::EV::HTTP->new($socket);
	$this->{_QUIET} = (delete $prm{quiet}) || 0;
	$this->{_LWP} = (delete $prm{lwp}) || $LWP;
	$this->bindip(delete $prm{bindip}) if defined $prm{bindip};
	$this->timeout(delete $prm{timeout}) if defined $prm{timeout};
	$this->timeout_con(delete $prm{timeout_conn}) if defined $prm{timeout_conn};
	$this->repeat(delete $prm{repeat}) if defined $prm{repeat};
	$this->repeat_conn(delete $prm{repeat_conn}) if defined $prm{repeat_conn};
	$this->user_agent(delete $prm{user_agent}) if defined $prm{user_agent};
	$this->user_agent(delete $prm{ua}) if defined $prm{ua};
	$this->meta_refresh(delete $prm{meta_refresh}) if defined $prm{meta_refresh};
	$this->redirect(delete $prm{redirect}) if defined $prm{redirect};
	
	
	my $trace =0;
	$trace= $trace | 1 if $TRACE_URL or $TRACE;
	$trace= $trace | 2 if $TRACE_REQUEST;
	$trace= $trace | 4 if $TRACE_RESPONSE;
	
	if (defined $prm{trace}) {
		$trace =0;
		my $str_trace = delete $prm{trace};
		$trace = $trace | 1 if $str_trace =~ /url/;
		$trace = $trace | 2 if $str_trace =~ /request/;
		$trace = $trace | 4 if $str_trace =~ /response/;
	}
	
	if ($trace) {
		my $coro = $Coro::current.'';
		my ($id) = $coro =~ /\((.*?)\)/s;
		$this->{_HTTP}->id("#$id");
		$this->{_HTTP}->trace($trace);
	}
	
	if (scalar keys %prm) {
		my @prm_names = keys %prm;
		die("unsupported parameter '$prm_names[0]'");
	}
	
	return $this;
}
sub child {
	my ($this, $get) = @_;
	
	my $child = {};
	bless $child, ref $this;
		
	while (my ($key, $val) = each(%$this)) {
		$child->{$key} = $val;
	}
	$child->{_HTTP} = $this->{_HTTP}->child;
	
	return $child if !$get;
	
	return $child->get($get);
}

$EV::DIED = sub {
	print "FATAL ERROR: $@\n";
	print "STACK TRACE: \n";
	foreach (1..5) {
		print join(' ',(caller($_))),"\n";
	}
	exit(1);
};


sub errc {$_[0]->{_HTTP}->errc}
sub errm {$_[0]->{_HTTP}->errm}

sub callback {
	my ($this, $http, $body, $url, $code) = @_; 

	delete $this->{_ROUSE_CB};
	if (!defined($body)) {
		if ($this->{_QUIET}) {
			$@ = $this->errm."(code: ".$this->errc.")";
			return;
		}
		croak($this->errm."(code: ".$this->errc.")");
	}
	
	return wantarray?($code,$url,$body):$body if !$this->{_LWP};
	return $http->response;
}


sub _rouse_cb {
	my ($this) = @_;
	$this->{_ROUSE_CB} = Coro::rouse_cb;
}

sub request {
	my ($this, $request) = @_;	# object of HTTP::Request
	$this->{_HTTP}->request($request, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}
sub pure_request {
	my ($this, $request) = @_;	# object of HTTP::Request
	$this->{_HTTP}->pure_request($request, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}
sub set_def {$_[0]->{_HTTP}->set_def($_[1]);}


sub get {
	my ($this, $url) = @_;	
	$this->{_HTTP}->get($url, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}

sub form {
	my ($this, $data, $formsign, $addtext) = @_;
	if (!$this->{_HTTP}->form($data, $formsign, $addtext, $this->_rouse_cb)) {
		$this->{_HTTP}->{_ERRC} = 11;
		$this->{_HTTP}->{_ERRM} = "no html form found";
		return $this->callback(undef, undef, "no html form found", 11);
	}
	$this->callback(Coro::rouse_wait);
}
sub form_get {$_[0]->{_HTTP}->form_get($_[1], $_[2])}
sub form_fill {$_[0]->{_HTTP}->form_fill($_[1], $_[2])}

sub DESTROY {
	my ($this) = @_;
	$this->{_HTTP}->stop if defined $this->{_HTTP}; # check for global destructor
}
# if you want to break connection manually
sub stop {
	my ($this) = @_;
	$this->{_HTTP}->stop;
	$this->{_ROUSE_CB}->($this->{_HTTP}, undef, "stoped by user", 0) if defined $this->{_ROUSE_CB};
}

sub body {$_[0]->{_HTTP}->body($_[1])}
sub page {$_[0]->{_HTTP}->body($_[1])}
sub url {$_[0]->{_HTTP}->{_URL}}
sub code {$_[0]->{_HTTP}->{_RESPONSE_VARS}->[1]}
sub id {$_[0]->{_HTTP}->id($_[1])}
sub socket {$_[0]->{_HTTP}->socket}

sub bindip {$_[0]->{_HTTP}->bindip($_[1])}
sub timeout {$_[0]->{_HTTP}->timeout($_[1])}
sub timeout_conn {$_[0]->{_HTTP}->timeout_conn($_[1])}
sub repeat {$_[0]->{_HTTP}->repeat($_[1])}
sub repeat_conn {$_[0]->{_HTTP}->{_SOCKET}->repeat($_[1])}

sub redirect {$_[0]->{_HTTP}->redirect($_[1])}
sub meta_refresh {$_[0]->{_HTTP}->meta_refresh($_[1])}
sub user_agent {$_[0]->{_HTTP}->user_agent($_[1])}
sub protocol {$_[0]->{_HTTP}->protocol($_[1])}
sub referer {$_[0]->{_HTTP}->referer($_[1])}

sub cookies {$_[0]->{_HTTP}->cookies($_[1])}
sub response {$_[0]->{_HTTP}->response}

sub response_headers {$_[0]->{_HTTP}->response_headers()}
sub request_header {$_[0]->{_HTTP}->request_header($_[1],$_[2])}
1;